/*
Revision History:
Author                          Date          Version     Description of Changes
------------------------------------------------------------------------------------------
Jongwon Seo				 	 2010/02/20 	    1.0				 Created
neryong@gmail.com
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <string>
using namespace std;
#include <ip_trie.h>

using namespace neryong;

inline uint32_t GetTickCount()
{
	int				iRet;
	struct timeval	tp;

	iRet = gettimeofday(&tp, NULL);
	return (tp.tv_sec * 1000 + tp.tv_usec / 1000);
}

typedef bit_trie<4,string,base_stride_util<4> >		ipt;
//typedef multi_bit_trie<4,string,base_stride_util<4> >	mipt;
typedef ipv4_multi_trie<string,base_stride_util<4> >	mipt;

class add_result
{
public:
	inline	void	add(const vector<string>& r)
	{
		for ( vector<string>::const_iterator i = r.begin() ; i != r.end() ; i++ )
			_r.push_back(*i);
	}
	vector<string> _r;
};

void output_ip(char* out,uint32_t ip)
{
	sprintf(out,"%d.%d.%d.%d",
		(ip>>24)&0xff,
		(ip>>16)&0xff,
		(ip>>8)&0xff,
		(ip)&0xff
		);
}

void find_test(const mipt& x,uint32_t ip)
{
	char szip[100];
	output_ip(szip,ip);
	printf("IP %s : ",szip);
	
	add_result r;
	x.find(ip, r);
	if ( r._r.size() )
	{	for ( vector<string>::const_iterator i = r._r.begin() ; i != r._r.end() ; i++ )
			printf("%s ",i->c_str());
		printf("\n");
	}
	else
		printf("not found\n");
}

void output_coutry(const mipt& x)
{
	printf("Find test\n");
	find_test(x,0x01000000);
	find_test(x,0x01FFFFFF);
	find_test(x,0x02222222);
	find_test(x,0x03333333);
	find_test(x,0x04444444);
	find_test(x,0x0500007f);
	find_test(x,0x05000080);
	find_test(x,0x05000100);
}

void insert_to_map(mipt& x,uint32_t ip,uint32_t mask,const string value)
{
	char szip[100];
	output_ip(szip,ip);
	printf("IP %s/%d is mapped to %s\n",szip,mask,value.c_str());
	x.insert_by_mask(ip,mask,value);
}

void erase_to_map(mipt& x,uint32_t ip,uint32_t mask,const string value)
{
	char szip[100];
	output_ip(szip,ip);
	printf("IP %s/%d is unmapped to %s\n",szip,mask,value.c_str());
	x.erase_by_mask(ip,mask,value);
}

void test_ip_coutry()
{
	uint32_t ip;
	mipt x;
	
	insert_to_map(x,0x01000000,8,"Korea");
	insert_to_map(x,0x02000000,8,"China");
	insert_to_map(x,0x03000000,8,"Japan");
	insert_to_map(x,0x04000000,8,"Korea");
	insert_to_map(x,0x04000000,8,"Japan");
	insert_to_map(x,0x05000000,25,"China");
	insert_to_map(x,0x05000080,25,"Korea");

	output_coutry(x);

	printf("\n");
	erase_to_map(x,0x04000000,8,"Japan");

	output_coutry(x);
}


class ipv4_pattern_map : public ipv4_multi_trie<int,base_stride_util<4> >
{
public:
	class process_result
	{
	public:
		inline	void	add(const vector<int>& r)
		{	for ( vector<int>::const_iterator i = r.begin() ; i != r.end() ; i++ )
			{	*_flag |= *i;
			}
		}
		int*		_flag;
	};
	inline	int		find(uint32_t ip) const
	{
		int flag = 0;
		process_result r;
		r._flag = &flag;
		ipv4_multi_trie<int,base_stride_util<4> >::find(ip,r);
		return flag;
	}
};

void output_flags(const ipv4_pattern_map& t,uint32_t base_ip,uint32_t count)
{
	char szip[100];
	for ( uint32_t i = 0 ; i < count ; i++ )
	{
		uint32_t ip = base_ip + i;
		output_ip(szip,ip);
		int r = t.find(ip);
		printf("%s : %x\n",szip,r);
	}
}

void test_ip_bit()
{
	printf("\n");
	ipv4_pattern_map t;
	
	char from[100];
	char to[100];
	
	uint32_t base_ip = 0x01010110;
	output_ip(from,base_ip);
	output_ip(to,base_ip+7-1);
	printf("Add weight %d where %s ~ %s\n",1,from,to);
	t.insert_by_count(base_ip,7,1);

	output_ip(from,base_ip);
	output_ip(to,base_ip+3-1);
	printf("Add weight %d where %s ~ %s\n",2,from,to);
	t.insert_by_count(base_ip,3,2);
	
	output_flags(t,base_ip,8);

	output_ip(from,base_ip);
	output_ip(to,base_ip+3-1);
	printf("Remove weight %d where %s ~ %s\n",2,from,to);
	t.erase_by_count(base_ip,3,2);

	output_flags(t,base_ip,8);

	output_ip(from,base_ip);
	output_ip(to,base_ip+7-1);
	printf("Remove weight %d where %s ~ %s\n",1,from,to);
	t.erase_by_count(base_ip,7,1);

	output_flags(t,base_ip,8);
}

int main(int argc,char **argv)
{
	uint32_t elapsed;
	
	printf("Begin test\n");
	
	test_ip_coutry();
	test_ip_bit();
	
	return 0;
}
